﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FantastPlatform.Framework.Core;
using NHibernate;
using NHibernate.Metadata;
using FantastPlatform.Framework.IService;
using NHibernate.Criterion;
using FantastPlatform.Framework.Common;
using FantastPlatform.Framework.Exception;
using System.Reflection;
using System.Collections;
using NHibernate.Type;
using System.Data.SqlClient;
using System.Data;

namespace FantastPlatform.Framework.Service
{
    public abstract class DataService<IEntity, Entity> : IDataService<IEntity>
        where IEntity : class, IItem
        where Entity : class, IEntity, new()
    {
        public DataService()
        {
            Session = SessionFactory.CreateSession();
        }

        public DataService(ISession session)
        {
            if (session == null)
                throw new ArgumentNullException();
            Session = session;
        }

        public ISession Session { get; set; }

        #region IBaseService<IEntity> 成员

        public virtual IEntity Get(object Id)
        {
            return Session.Get<Entity>(Id) as IEntity;
        }

        public virtual IEntity Load(object Id)
        {
            return Session.Load<Entity>(Id) as IEntity;
        }

        public virtual IList<IEntity> Search(ConditionGroup conditionGroup, IList<Sort> sorts, int pageSize, int page, out int totalCount)
        {
            ICriteria criteria = Session.CreateCriteria(typeof(Entity));
            if (conditionGroup != null)
            {
                criteria = CreateCriteria(criteria, conditionGroup);
            }
            IList<IEntity> list = CriteriaSortsAndPage(criteria,sorts, pageSize, page, out totalCount);
             return list;
        }

        protected ICriteria AddCriteriaAlias(ICriteria criteria, string[] fields)
        {
            StringBuilder sb=new StringBuilder();
            StringBuilder sbb=new StringBuilder();
            for (int i = 0; i < fields.Length-1; i++)
            {
                sb.Append(i==0?fields[i]:"."+fields[i]);
                sbb.Append(fields[i]);
                criteria = criteria.CreateAlias(sb.ToString(), sbb.ToString());
            }
            return criteria;
        }

        protected string GetField(ConditionGroup conditionGroup)
        {
            string field=null;
            List<Condition> condtions = new List<Condition>();
            condtions = GetConditions(conditionGroup, condtions);
            if (!CollectionHelper.IsNullOrEmpty<Condition>(condtions))
            {
                 List<string> list= condtions.Select(c => c.Field).ToList();
                 int temp = 0;
                 int f = 0;
                 for (int i = 0; i < list.Count; i++)
                 {
                     if (list[i].Length > temp)
                     {
                         temp = list[i].Length;
                         f = i;
                     }
                 }
                 field = list[f];
            }
            return field;
         }
        protected List<Condition> GetConditions(ConditionGroup conditionGroup,List<Condition> conditions)
        {
            List<Condition> current = new List<Condition>();
            current.AddRange(conditionGroup.Conditions);
            conditions = current;
            if (!CollectionHelper.IsNullOrEmpty<ConditionGroup>(conditionGroup.Children))
            {
                foreach (ConditionGroup c in conditionGroup.Children)
                {
                    if (c != null)
                    {
                        conditions.AddRange(GetConditions(c, conditions));
                    }
                }
            }
            return conditions;
        }
        protected ICriteria CreateCriteria(ICriteria criteria, ConditionGroup conditionGroup)
        {
            CheckConditionGroup(conditionGroup);
            string field = GetField(conditionGroup);
            string[] data = field.Split(".".ToCharArray());
            if (data.Length>1)
            {
                criteria = AddCriteriaAlias(criteria, data);
            }
            ICriterion c = AddCriterionByConditionGroup(conditionGroup, null);
            if (c != null)
            {
                criteria.Add(c);
            }
            return criteria;
        }
        private void CheckConditionGroup(ConditionGroup conditionGroup)
        {
            Check.Require<InvalidCodeException>(conditionGroup.Relation == Relations.Null, "第一个查询组关系不能是or或and");
         //   Check.Require<InvalidCodeException>(CollectionHelper.IsNullOrEmpty<Condition>(conditionGroup.Conditions) != null, "查询组下必须有查询条件");
          //  Check.Require<InvalidCodeException>(conditionGroup.Conditions[0].Relation == Relations.Null, "第一个查询条件关系不能是or或and");
            if (!CollectionHelper.IsNullOrEmpty<ConditionGroup>(conditionGroup.Children))
                CheckChildrenCoditionGroup(conditionGroup.Children);
        }
        private void CheckChildrenCoditionGroup(IList<ConditionGroup> conditionGroups)
        {
            foreach (ConditionGroup c in conditionGroups)
            {
                Check.Require<InvalidCodeException>(c.Relation != Relations.Null, "查询组关系不能NULL");
             //   Check.Require<InvalidCodeException>(CollectionHelper.IsNullOrEmpty<Condition>(c.Conditions) != null, "查询组下必须有查询条件");
            //    Check.Require<InvalidCodeException>(c.Conditions[0].Relation == Relations.Null, "查询组的第一查询条件关系不能是Or或and");
                if (CollectionHelper.IsNullOrEmpty<ConditionGroup>(c.Children))
                {
                    CheckChildrenCoditionGroup(c.Children);
                }
            }
        }
        protected ICriterion AddCriterionByConditionGroup(ConditionGroup conditionGroup, ICriterion result)
        {
            ICriterion current = AddCriterionByConditions(conditionGroup.Conditions);
            if (result != null)
            {
                switch (conditionGroup.Relation)
                {
                    case Relations.Null:
                        result = current;
                        break;
                    case Relations.And:
                        result = Restrictions.And(result, current);
                        break;
                    case Relations.Or:
                        result = Restrictions.Or(result, current);
                        break;
                    default:
                        break;
                }
            }
            else
            {
                result = current;
            }
            if (!CollectionHelper.IsNullOrEmpty<ConditionGroup>(conditionGroup.Children))
            {
                foreach (ConditionGroup c in conditionGroup.Children)
                {
                    result = AddCriterionByConditionGroup(c, result);
                }
            }
            return result;
        }
        protected ICriterion AddCriterionByConditions(IList<Condition> conditions)
        {
            ICriterion result = null;
            ICriterion current = null;
            if (!CollectionHelper.IsNullOrEmpty<Condition>(conditions))
            {
                foreach (Condition condition in conditions)
                {

                    switch (condition.Comparison)
                    {
                        case Comparisons.Eq:
                            current = Restrictions.Eq(ParseField(condition.Field), condition.Value1);
                            break;
                        case Comparisons.Gt:
                            current = Restrictions.Gt(ParseField(condition.Field), condition.Value1);
                            break;
                        case Comparisons.Lt:
                            current = Restrictions.Lt(ParseField(condition.Field), condition.Value1);
                            break;
                        case Comparisons.Between:
                            current = Restrictions.Between(ParseField(condition.Field), condition.Value1, condition.Value2);
                            break;
                        case Comparisons.Like:
                            current = Restrictions.Like(ParseField(condition.Field), condition.Value1);
                            break;
                        case Comparisons.NotEq:
                            current = Restrictions.Not(Expression.Eq(ParseField(condition.Field), condition.Value1));
                            break;
                        default:
                            break;
                    }
                    switch (condition.Relation)
                    {
                        case Relations.Null:
                            result = current;
                            break;
                        case Relations.And:
                            if (result != null)
                                result = Restrictions.And(result, current);
                            break;
                        case Relations.Or:
                            if (result != null)
                                result = Restrictions.Or(result, current);
                            break;
                        default:
                            break;
                    }
                }
            }

            return result;
        }
        protected string ParseField(string field)
        {
            string[] fields = field.Split(".".ToCharArray());
            if (fields.Length > 1)
            {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < fields.Length-1; i++)
                {
                    sb.Append(fields[i]);
                }
                field = sb.Append("." + fields[fields.Length - 1]).ToString();
            }
            return field;
        }
        public ICriteria AddCriterionByConditions(ICriteria criteria, ConditionGroup conditionGroup)
        {
           
            return criteria;
        }
        protected ICriteria AddOrderBySorts(ICriteria criteria, IList<Sort> sorts)
        {
            if (!CollectionHelper.IsNullOrEmpty<Sort>(sorts))
            {
                foreach (Sort sort in sorts)
                {
                    switch (sort.Order)
                    {
                        case Orders.Desc:
                            criteria.AddOrder(Order.Desc(sort.Field));
                            break;
                        case Orders.Asc:
                            criteria.AddOrder(Order.Asc(sort.Field));
                            break;
                        default:
                            break;
                    }
                }
            }
            return criteria;
        }

        protected virtual IList<IEntity> CriteriaSortsAndPage(ICriteria criteria, IList<Sort> sorts, int pageSize, int page, out int totalCount)
        {
            ICriteria clone = criteria.Clone() as ICriteria;
            criteria = AddOrderBySorts(criteria, sorts);
            IList<IEntity> list = criteria.SetFirstResult((page - 1) * pageSize).SetMaxResults(pageSize).List<IEntity>();
            totalCount = int.Parse(clone.SetProjection(Projections.RowCount()).UniqueResult().ToString());
            return list;
        }

        protected virtual void DefaultValue(IEntity entity)
        {
            //entity.Id = GetAutoId(entity.GetType().Name);
            entity.Guid = Guid.NewGuid();
            entity.CreateDate = DateTime.Now;
            entity.Enable = true;
        }
        private int GetAutoId(string entityType)
        {
            int id = 0;
            try
            {
                string sql = System.Configuration.ConfigurationManager.ConnectionStrings["GpsPatrol"].ToString();
                SqlConnection conn = new SqlConnection(sql);
                SqlCommand command = new SqlCommand("S_GET_SEQ_CUR_VAL", conn);
                command.CommandType = System.Data.CommandType.StoredProcedure;

                command.Parameters.Add("@SEQ_NAME", SqlDbType.VarChar);
                command.Parameters["@SEQ_NAME"].Value = entityType;

                //添加输出参数
                command.Parameters.Add("@CURVAL", SqlDbType.Int);
                command.Parameters["@CURVAL"].Direction = ParameterDirection.Output;
                conn.Open();
                SqlDataReader reader = command.ExecuteReader();
                id = Convert.ToInt32(command.Parameters["@CURVAL"].Value);
            }
            catch(System.Exception ex)
            {
            }
            return id;
        }
        public virtual object Create(IEntity entity)
        {
            object id = 0;
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
                {
                    DefaultValue(entity);
                    id = Session.Save(entity);
                }
                , transaction);
            return id;
        }

        public virtual void Update(IEntity entity)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
                {
                    
                    entity.ModifyDate = DateTime.Now;
                    entity.Enable = true;
                    Session.Update(entity);
                }, transaction);
        }

        //public virtual void Delete(IEntity entity)
        //{
        //    ITransaction transaction = null;
        //    ExecuteTransaction((ITransaction tran) => Session.Delete(entity), transaction);
        //}         
        public virtual void SaveOrUpdate(IEntity entity)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
                {
                    entity.ModifyDate = DateTime.Now;
                    entity.Enable = true;
                    Session.SaveOrUpdate(entity);
                }, transaction);
        }

        public virtual void Refresh(IEntity entity)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) => Session.Refresh(entity), transaction);
        }

        public bool IsExisted(int id)
        {
            IEntity entity = Get(id);
            return entity != null;
        }
        #endregion

        protected delegate void TransactionDelegate(ITransaction tran);
        protected virtual void ExecuteTransaction(TransactionDelegate transaction,ITransaction tran)
        {
            try
            {
                tran = Session.BeginTransaction();
                transaction(tran);
                tran.Commit();
            }
            catch (System.Exception e)
            {
                if (tran != null)
                    tran.Rollback();
                throw e;
            }
            finally
            {
                if (Session != null)
                    Session.Close();
            }

        }

        #region IDataService<IEntity> 成员

        protected virtual bool IsUsing(IEntity entity)
        {
            bool isusing = false;
            IClassMetadata metadata = Session.SessionFactory.GetClassMetadata(entity.GetType().FullName);
            foreach (string propertyName in metadata.PropertyNames)
            {
                if (metadata.GetPropertyType(propertyName).IsCollectionType)
                {
                    PropertyInfo info = entity.GetType().GetProperty(propertyName);
                    if (!string.IsNullOrEmpty(info.GetValue(entity, null).ToString()))
                    {
                        isusing = true;
                        break;
                    }
                }
            }
            return isusing;
        }
        protected virtual bool IsUsing(int id)
        {
            IEntity entity = Session.Get<Entity>(id);
            bool isusing = false;
            IClassMetadata metadata = Session.SessionFactory.GetClassMetadata(entity.GetType().FullName);
            foreach (string propertyName in metadata.PropertyNames)
            {
                if (metadata.GetPropertyType(propertyName).IsCollectionType)
                {
                    PropertyInfo info = entity.GetType().GetProperty(propertyName);
                    if (!string.IsNullOrEmpty(info.GetValue(entity, null).ToString()))
                    {
                        isusing = true;
                        break;
                    }
                }
            }
            return isusing;
        }
        #endregion

        #region IDataService<IEntity> 成员

        public virtual void Delete(List<int> ids)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
            {
                foreach (int id in ids)
                {
                    IEntity entity = Session.Get<Entity>(id) as IEntity;
                    entity.ModifyDate = DateTime.Now;
                    entity.Enable = false;
                    Session.Update(entity);
                }
            }, transaction);
        }
        public virtual void DeleteReally(List<int> ids)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
            {
                foreach (int id in ids)
                {
                    IEntity entity = Session.Get<Entity>(id) as IEntity;
                    Check.Require<ArgumentNullException>(entity != null, string.Format("当前对象不存在！"));
                    Session.Delete(entity);
                }
            }, transaction);
        }

        protected virtual void DeleteReallyAndRelations(List<int> ids, List<string> tables)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
            {
                foreach (int id in ids)
                {
                    IEntity entity = Session.Get<Entity>(id) as IEntity;
                    Check.Require<ArgumentNullException>(entity != null, string.Format("当前对象不存在！"));
                    if (!CollectionHelper.IsNullOrEmpty<string>(tables))
                    {
                        foreach (string s in tables)
                        {
                            Session.CreateSQLQuery(string.Format("delete from {0} where {1}Id={2}",s,typeof(Entity).Name,id)).ExecuteUpdate();
                        }
                    }
                        Session.Delete(entity);
                }
            }, transaction);
        }
        #endregion

        #region IDataService<IEntity> 成员


        public virtual void RollBack(List<int> ids)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
            {
                foreach (int id in ids)
                {
                    IEntity entity = Session.Get<Entity>(id) as IEntity;
                    entity.Enable = true;
                    Session.Update(entity);
                }
            }, transaction);
        }

        #endregion

        #region IDataService<IEntity> 成员


        public virtual void Delete(int id)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
            {

                IEntity entity = Session.Get<Entity>(id) as IEntity;
                entity.Enable = false;
                Session.Update(entity);

            }, transaction);
        }
        public virtual void DeleteReally(int id)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
            {
                IEntity entity = Session.Get<Entity>(id) as IEntity;
                Check.Require<ArgumentNullException>(entity != null, string.Format("当前对象不存在！"));
                Session.Delete(entity);
            }, transaction);
        }
        public virtual void RollBack(int id)
        {
            ITransaction transaction = null;
            ExecuteTransaction((ITransaction tran) =>
            {
                IEntity entity = Session.Get<Entity>(id) as IEntity;
                entity.Enable = true;
                Session.Update(entity);
            }, transaction);
        }
        #endregion

        #region IDataService<IEntity> 成员


        public virtual IList<IEntity> LoadList(IList<Sort> sorts, int pageSize, int page, out int totalCount)
        {
            ICriteria criteria = Session.CreateCriteria(typeof(Entity)).Add(Expression.Eq("Enable",true));
            IList<IEntity> list = CriteriaSortsAndPage(criteria,sorts, pageSize, page, out totalCount);
            return list;
        }

        public virtual IList<IEntity> LoadLisDeleted(IList<Sort> sorts, int pageSize, int page, out int totalCount)
        {
            ICriteria criteria = Session.CreateCriteria(typeof(Entity)).Add(Expression.Eq("Enable",false));
            IList<IEntity> list = CriteriaSortsAndPage(criteria,sorts, pageSize, page, out totalCount);
            return list;
        }

        #endregion
         
        #region IDataService<IEntity> 成员

        #endregion
        #region 通过属性查询，内部使用
        protected IList<IEntity> GetEntitiesByObject(object entity)
        {
            ICriteria criteria = AddExample(null, entity, "", typeof(Entity).FullName);
            return criteria.List<IEntity>();
        }
        protected ICriteria AddExample(ICriteria criteria, Object entity, String parent, String entityName)
        {
            string alias = string.Empty;
            if (criteria == null)
            {
                alias = typeof(Entity).Name;
                criteria = Session.CreateCriteria(entityName, alias);
            }
            else
            {
                alias = parent + "." + entityName;
                if (entityName != null)
                    criteria = criteria.GetCriteriaByAlias(parent).CreateCriteria(entityName, alias);

            }

            if (entity != null)
            {
                criteria.Add(Example.Create(entity).IgnoreCase());

                foreach (var p in entity.GetType().GetProperties().Where(c => !c.PropertyType.FullName.StartsWith("System")))
                {
                    Object sub = p.GetValue(entity, null);

                    criteria = AddExample(criteria, sub, alias, p.Name);
                }
            }
            return criteria;
        }
        protected string SortByOrder(string hql, IList<Sort> sorts, string alias)
        {
            if (!CollectionHelper.IsNullOrEmpty<Sort>(sorts))
            {
                StringBuilder hqlStringBuilder = new StringBuilder(hql);
                hqlStringBuilder.Append(" order by ");
                foreach (Sort sort in sorts)
                {
                    if (sort.Order == Orders.Asc)
                    {
                        hqlStringBuilder.Append(alias + "." + sort.Field + ", ");
                    }
                    else
                    {
                        hqlStringBuilder.Append(alias + "." + sort.Field + " desc, ");
                    }
                }
                hqlStringBuilder.Remove(hqlStringBuilder.Length - 2, 2);
                return hqlStringBuilder.ToString();
            }
            else
            {
                return hql;
            }
        }
        #endregion
    }
}
